<!DOCTYPE html>
  <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Face Detection</title>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
  <script src='https://fustyles.github.io/webduino/TensorFlow/Face-api/face-api.min.js'></script>
  </head>
  <body>
  <div id="container"></div>
  <img id="ShowImage" src="" style="display:none" crossorigin="anonymous">
  <canvas id="canvas" style="display:none"></canvas>  
  <table>
  <tr>
    <td><button id="restart" onclick="try{fetch(document.location.origin+'/control?restart');}catch(e){}">Restart</button></td> 
    <td colspan="2"><input type="button" id="getStill" value="Get Still" style="display:none"></td> 
  </tr>
  <tr>
    <td>MirrorImage</td> 
    <td colspan="2">  
      <select id="mirrorimage">
        <option value="1">Y</option>
        <option value="0">N</option>
      </select>
    </td>
  </tr>       
  <tr>
    <td>Flash</td>
    <td colspan="2"><input type="range" id="flash" min="0" max="255" value="0"></td>
  </tr>
  <tr>
    <td>Quality</td>
    <td colspan="2"><input type="range" id="quality" min="10" max="63" value="10"></td>
  </tr>
  <tr>
    <td>Brightness</td>
    <td colspan="2"><input type="range" id="brightness" min="-2" max="2" value="0"></td>
  </tr>
  <tr>
    <td>Contrast</td>
    <td colspan="2"><input type="range" id="contrast" min="-2" max="2" value="0"></td>
  </tr>
  <tr>
    <td>Resolution</td> 
    <td colspan="2">
      <select id="framesize">
        <option value="10">UXGA(1600x1200)</option>
        <option value="9">SXGA(1280x1024)</option>
        <option value="8">XGA(1024x768)</option>
        <option value="7">SVGA(800x600)</option>
        <option value="6">VGA(640x480)</option>
        <option value="5">CIF(400x296)</option>
        <option value="4" selected="selected">QVGA(320x240)</option>
        <option value="3">HQVGA(240x176)</option>
        <option value="0">QQVGA(160x120)</option>
      </select> 
    </td>
  </tr>
  <tr>
    <td>Rotate</td>
    <td align="left" colspan="2">
        <select onchange="document.getElementById('canvas').style.transform='rotate('+this.value+')';">
          <option value="0deg">0deg</option>
          <option value="90deg">90deg</option>
          <option value="180deg">180deg</option>
          <option value="270deg">270deg</option>
        </select>
    </td>
  </tr>  
  </table>
  <div id="message" style="color:red">Please wait for loading model.<div>
  </body>
  </html> 
  
  <script>
    var restart = document.getElementById('restart');
    var getStill = document.getElementById('getStill');
    var ShowImage = document.getElementById('ShowImage');
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d"); 
    var mirrorimage = document.getElementById("mirrorimage");  
    var message = document.getElementById('message');
    var flash = document.getElementById('flash'); 
    var myTimer;
    var restartCount=0;
    //Model: https://github.com/fustyles/webduino/tree/master/TensorFlow/Face-api
    const modelPath = 'https://fustyles.github.io/webduino/TensorFlow/Face-api/';
    let currentStream;
    let displaySize = { width:320, height: 240 }
    let faceDetection;
    
    Promise.all([
      faceapi.nets.tinyFaceDetector.load(modelPath),
      faceapi.nets.faceLandmark68TinyNet.load(modelPath),
      faceapi.nets.faceRecognitionNet.load(modelPath),
      faceapi.nets.faceExpressionNet.load(modelPath),
      faceapi.nets.ageGenderNet.load(modelPath)          
    ]).then(function(){
      message.innerHTML = "";
      getStill.style.display = "block";
      getStill.click();
    })
    
    getStill.onclick = function (event) {
      clearInterval(myTimer);  
      myTimer = setInterval(function(){error_handle();},5000);
      ShowImage.src=location.origin+'/capture?'+Math.random();
    }

    function error_handle() {
      restartCount++;
      clearInterval(myTimer);
      if (restartCount<=2) {
        message.innerHTML = "Get still error. <br>Restart ESP32-CAM "+restartCount+" times.";
        myTimer = setInterval(function(){getStill.click();},10000);
      }
      else
        message.innerHTML = "Get still error. <br>Please close the page and check ESP32-CAM.";
    }    

    ShowImage.onload = function (event) {
      clearInterval(myTimer);
      restartCount=0;      
      canvas.setAttribute("width", ShowImage.width);
      canvas.setAttribute("height", ShowImage.height);
      canvas.style.display = "block";
      
      if (mirrorimage.value==1) {
        context.translate((canvas.width + ShowImage.width) / 2, 0);
        context.scale(-1, 1);
        context.drawImage(ShowImage, 0, 0, ShowImage.width, ShowImage.height);
        context.setTransform(1, 0, 0, 1, 0, 0);
      }
      else
        context.drawImage(ShowImage,0,0,ShowImage.width,ShowImage.height);
      DetectImage();        
    }     
    
    restart.onclick = function (event) {
      fetch(location.origin+'/control?restart');
    }    
    flash.onchange = function (event) {
      fetch(location.origin+'/control?flash='+this.value);
    } 
    framesize.onclick = function (event) {
      fetch(document.location.origin+'/control?var=framesize&val='+this.value);
      setTimeout(function(){getStill.click();},1000);
    }  
    quality.onclick = function (event) {
      fetch(document.location.origin+'/control?var=quality&val='+this.value);
    } 
    brightness.onclick = function (event) {
      fetch(document.location.origin+'/control?var=brightness&val='+this.value);
    } 
    contrast.onclick = function (event) {
      fetch(document.location.origin+'/control?var=contrast&val='+this.value);
    }                             
    
    async function DetectImage() {
      const detections = await faceapi.detectAllFaces(canvas, new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks(true).withFaceExpressions().withAgeAndGender()
      const resizedDetections = faceapi.resizeResults(detections, displaySize)
      faceapi.draw.drawDetections(canvas, resizedDetections)
      faceapi.draw.drawFaceLandmarks(canvas, resizedDetections)
      faceapi.draw.drawFaceExpressions(canvas, resizedDetections)
      resizedDetections.forEach(result => {
        const { detection,expressions,gender,genderProbability,age } = result
        //message.innerHTML = JSON.stringify(result);
        
        var i=0;
        message.innerHTML ="";
        var maxEmotion="neutral";
        var maxProbability=expressions.neutral;
        if (expressions.happy>maxProbability) {
          maxProbability=expressions.happy;
          maxEmotion="happy";
        }
        if (expressions.sad>maxProbability) {
          maxProbability=expressions.sad;
          maxEmotion="sad";
        }
        if (expressions.angry>maxProbability) {
          maxProbability=expressions.angry;
          maxEmotion="angry";
        }
        if (expressions.fearful>maxProbability) {
          maxProbability=expressions.fearful;
          maxEmotion="fearful";
        }
        if (expressions.disgusted>maxProbability) {
          maxProbability=expressions.disgusted;
          maxEmotion="disgusted";
        }
        if (expressions.surprised>maxProbability) {
          maxProbability=expressions.surprised;
          maxEmotion="surprised";
        }
        message.innerHTML+= i+",age,"+Math.round(age)+",gender,"+gender+",genderProbability,"+Round(genderProbability)+",emotion,"+maxEmotion+",neutral,"+Round(expressions.neutral)+",happy,"+Round(expressions.happy)+",sad,"+Round(expressions.sad)+",angry,"+Round(expressions.angry)+",fearful,"+Round(expressions.fearful)+",disgusted,"+Round(expressions.disgusted)+",surprised,"+Round(expressions.surprised)+",boxX,"+Round(detection._box._x)+",boxY,"+Round(detection._box._y)+",boxWidth,"+Round(detection._box._width)+",boxHeight,"+Round(detection._box._height)+"<br>";
                
        new faceapi.draw.DrawTextField(
          [
            `${faceapi.round(age, 0)} years`,
            `${gender} (${faceapi.round(genderProbability)})`
          ],
          result.detection.box.bottomRight
        ).draw(canvas)
      })
      try { 
        document.createEvent("TouchEvent");
        setTimeout(function(){getStill.click();},250);
      }
      catch(e) { 
        setTimeout(function(){getStill.click();},150);
      } 
    }   
    function Round(n) {
      return Math.round(Number(n)*100)/100;
    }      
  </script>   
